package org.zoomdev.zoom.protobuf.modules;

import io.protostuff.ByteArrayInput;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffOutput;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Creators;
import org.zoomdev.zoom.serializer.Serializer;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;

final class Serializers {


    static final class SchemaSerializer<T> implements Serializer<T> {

        private final Schema<T> schema;

        public SchemaSerializer(Class<T> type) {
            this.schema = RuntimeSchema.getSchema(type);
        }

        @Override
        public byte[] serialize(T data) {
            return ProtostuffUtils.serialize(schema, data);
        }

        @Override
        public T deserialize(byte[] src) {
            return ProtostuffUtils.deserialize(schema, src);
        }
    }



    static class ArrayProtostuffSerializer implements Serializer {
        private final ISchema schema;
        private final Class<?> type;

        public ArrayProtostuffSerializer(Class<?> type) {
            this.type = type;
            this.schema = ISchemaFactory.createFromClass(type);
        }

        @Override
        public byte[] serialize(Object data) {
            try {
                LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
                ProtostuffOutput output = new ProtostuffOutput(buffer);
                int index = 1;
                Object[] array = (Object[]) data;
                for (Object value : array) {
                    if (value == null) {
                        continue;
                    }
                    schema.write(output, index++, value, false);
                }
                return output.toByteArray();
            } catch (IOException e) {
                throw new ZoomException(e);
            }
        }

        @Override
        public Object deserialize(byte[] bytes) {
            try {
                ArrayList list = new ArrayList();
                ByteArrayInput input = new ByteArrayInput(bytes, 0, bytes.length, true);
                for (int number = input.readFieldNumber(null); number != 0; number = input.readFieldNumber(null)) {
                    list.add(schema.mergeFrom(input));
                }
                Object array = Array.newInstance(type, list.size());
                return list.toArray((Object[]) array);
            } catch (Exception e) {
                throw new ZoomException(e);
            }
        }
    }

    static class ListProtostuffSerializer implements Serializer {
        private final ISchema schema;
        private final Class<?> type;

        public ListProtostuffSerializer(Class<?> type, ISchema schema) {
            this.type = type;
            this.schema = schema;
        }

        @Override
        public byte[] serialize(Object data) {
            try {
                LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
                ProtostuffOutput output = new ProtostuffOutput(buffer);
                int index = 1;
                for (Object value : (Iterable) data) {
                    if (value == null) {
                        continue;
                    }
                    schema.write(output, index++, value, false);
                }
                return output.toByteArray();
            } catch (IOException e) {
                throw new ZoomException(e);
            }
        }

        @Override
        public Object deserialize(byte[] bytes) {
            Collection list = Creators.newCollection(type);
            try {
                ByteArrayInput input = new ByteArrayInput(bytes, 0, bytes.length, true);
                for (int number = input.readFieldNumber(null); number != 0; number = input.readFieldNumber(null)) {
                    Object value = schema.mergeFrom(input);
                    list.add(value);
                }
                return list;
            } catch (Exception e) {
                throw new ZoomException(e);
            }

            //return ProtostuffUtils.deserializeList(type, schema, src);
        }
    }
}
